/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

using System;

namespace argot
{
	
	public class TypeLibrary
	{
		public static int NOTYPE = -1;
		
		public static int TYPE_NOT_DEFINED = 0;
		public static int TYPE_RESERVED = 1;
		public static int TYPE_REGISTERED = 2;
		public static int TYPE_COMPLETE = 3;
		
		private System.Collections.ArrayList _types;
		private System.Collections.Hashtable _names;
		private System.Collections.Hashtable _classes;
		
		private class TypeDefinition
		{
			public int state;
			public int id;
			public System.String name;
			public TypeReader reader;
			public TypeWriter writer;
			public System.Type clss;
			public TypeElement structure;
		}
		
		public TypeLibrary()
		{
			System.Console.Out.WriteLine("");
			System.Console.Out.WriteLine("Argot Version 1.2.0");
			System.Console.Out.WriteLine("Copyright 2003-2005 (C) Live Media Pty Ltd.");
			System.Console.Out.WriteLine("www.einet.com.au");
			
			_types = new System.Collections.ArrayList();
			_names = new System.Collections.Hashtable();
			_classes = new System.Collections.Hashtable();
		}
		
		private int Add(TypeDefinition definition)
		{
			_types.Add(definition);
			int id = _types.IndexOf(definition);
			definition.id = id;
			_names[definition.name] = definition;
			if (definition.clss != null)
			{
				if (_classes[definition.clss] != null)
					throw new TypeException("class overloaded");
				_classes[definition.clss] = definition;
			}
			return id;
		}
		
		private System.String CheckName(System.String name)
		{
			if (name == null || name.Length == 0)
				throw new TypeException("invalid parameter");
			
			return name.ToLower();
		}

		public virtual int GetTypeState(System.String uncheckedName)
		{
			System.String name;
			try
			{
				name = CheckName(uncheckedName);
			}
			catch (TypeException)
			{
				return TYPE_NOT_DEFINED;
			}
			
			TypeDefinition definition = (TypeDefinition) _names[name];
			if (definition == null)
				return TYPE_NOT_DEFINED;
			return definition.state;
		}
		
		public virtual int GetTypeState(int id)
		{
			TypeDefinition definition = (TypeDefinition) _types[id];
			if (definition == null)
				return TYPE_NOT_DEFINED;
			return definition.state;
		}
		
		public virtual int Register(System.String uncheckedName, TypeElement structure, TypeReader reader, TypeWriter writer, System.Type clss)
		{
			System.String name = CheckName(uncheckedName);
			
			if (structure == null)
				throw new TypeException("invalid parameter: structure is null");
			if (reader == null)
				throw new TypeException("invalid parameter: reader is null");
			if (writer == null)
				throw new TypeException("invalid parameter: writer is null");
			
			int state = GetTypeState(name);
			if (state != TYPE_NOT_DEFINED && state != TYPE_RESERVED)
			{
				throw new TypeNameDefinedException(name + " " + state);
			}
			
			TypeDefinition definition = null;
			
			if (state == TYPE_NOT_DEFINED)
			{
				definition = new TypeDefinition();
				definition.name = name;
				definition.structure = structure;
				definition.reader = reader;
				definition.writer = writer;
				definition.clss = clss; // Can be null.
				definition.state = TYPE_COMPLETE;
				
				Add(definition);
			}
			else if (state == TYPE_RESERVED)
			{
				definition = (TypeDefinition) _names[name];
				definition.structure = structure;
				definition.reader = reader;
				definition.writer = writer;
				definition.clss = clss;
				definition.state = TYPE_COMPLETE;
				
				// copied from add.  Need to add class.
				if (definition.clss != null)
				{
					if (_classes[definition.clss] != null)
						throw new TypeException("class overloaded");
					_classes[definition.clss] = definition;
				}
			}
			else
			{
				throw new TypeException("invalid state");
			}
			
			structure.Bind(this, structure, name, definition.id);
			
			return definition.id;
		}
		
		/// <summary> 
		/// Register a name with its structure.  Used when reading from
		/// a dictionary file.
		/// </summary>
		public virtual int Register(System.String uncheckedName, TypeElement structure)
		{
			System.String name = CheckName(uncheckedName);
			
			if (structure == null)
				throw new TypeException("invalid parameter");
			
			int state = GetTypeState(name);
			if (state != TYPE_NOT_DEFINED && state != TYPE_RESERVED)
			{
				throw new TypeNameDefinedException(name);
			}
			
			TypeDefinition definition = null;
			
			if (state == TYPE_NOT_DEFINED)
			{
				definition = new TypeDefinition();
				definition.name = name;
				definition.structure = structure;
				definition.reader = null;
				definition.writer = null;
				definition.clss = null; // Can be null.
				definition.state = TYPE_REGISTERED;
				
				Add(definition);
			}
			else if (state == TYPE_RESERVED)
			{
				definition = (TypeDefinition) _names[name];
				definition.structure = structure;
				definition.state = TYPE_REGISTERED;
				
				// copied from add.  Need to add class.
				if (definition.clss != null)
				{
					if (_classes[definition.clss] != null)
						throw new TypeException("class overloaded");
					_classes[definition.clss] = definition;
				}
			}
			else
			{
				throw new TypeException("invalid state");
			}
			
			structure.Bind(this, structure, name, definition.id);
			
			return definition.id;
		}
		
		/// <summary> 
		/// Reserve a name.  Used for circular references.
		/// </summary>

		public virtual int Reserve(System.String uncheckedName)
		{
			System.String name = CheckName(uncheckedName);
			
			if (GetTypeState(name) != TYPE_NOT_DEFINED)
			{
				throw new TypeNameDefinedException(name);
			}
			
			TypeDefinition definition = new TypeDefinition();
			definition.name = name;
			definition.structure = null;
			definition.reader = null;
			definition.writer = null;
			definition.clss = null; // Can be null.
			definition.state = TYPE_RESERVED;
			
			int id = Add(definition);
			return id;
		}
		
		public virtual int Bind(System.String uncheckedName, TypeReader reader, TypeWriter writer, System.Type clss)
		{
			System.String name = CheckName(uncheckedName);
			
			int state = GetTypeState(name);
			if (state != TYPE_REGISTERED)
				throw new TypeException("type in wrong state");
			
			TypeDefinition def = (TypeDefinition) _names[name];
			def.reader = reader;
			def.writer = writer;
			def.clss = clss;
			def.state = TYPE_COMPLETE;
			
			// copied from add.  Need to add class.
			if (def.clss != null)
			{
				if (_classes[def.clss] != null)
					throw new TypeException("class overloaded: " + name);
				_classes[def.clss] = def;
			}
			
			def.structure.Bind(this, def.structure, name, def.id);
			
			return def.id;
		}
		
		public virtual int GetId(System.String uncheckedName)
		{
			System.String name = CheckName(uncheckedName);
			TypeDefinition def = (TypeDefinition) _names[name];
			if (def == null)
				throw new TypeNotDefinedException(name);
			
			return def.id;
		}
		
		public virtual System.String GetName(int id)
		{
			TypeDefinition def = (TypeDefinition) _types[id];
			if (def == null)
				throw new TypeNotDefinedException("type not found");
			
			return def.name;
		}
		
		public virtual TypeElement GetStructure(int id)
		{
			TypeDefinition def = (TypeDefinition) _types[id];
			if (def == null)
				throw new TypeNotDefinedException("type id");
			
			if (def.state != TYPE_COMPLETE && def.state != TYPE_REGISTERED)
				throw new TypeException("type not complete: " + this.GetName(id));
			
			return def.structure;
		}
		
		
		public virtual TypeReader GetReader(int id)
		{
			TypeDefinition def = (TypeDefinition) _types[id];
			if (def == null)
				throw new TypeNotDefinedException("type id");
			
			if (def.state != TYPE_COMPLETE)
				throw new TypeException("type not complete: " + this.GetName(id));
			
			return def.reader;
		}
		
		public virtual TypeWriter GetWriter(int id)
		{
			TypeDefinition def = (TypeDefinition) _types[id];
			if (def == null)
				throw new TypeException("type not found");
			
			if (def.state != TYPE_COMPLETE)
				throw new TypeException("type not complete: " + this.GetName(id));
			
			return def.writer;
		}
		
		public virtual System.Type GetClass(int id)
		{
			TypeDefinition def = (TypeDefinition) _types[id];
			if (def == null)
				throw new TypeException("type not found");
			
			if (def.clss == null)
				throw new TypeException("no class bound for type");
			
			return def.clss;
		}
		
		public virtual int GetId(System.Type clss)
		{
			System.Type searchClass = clss;
									
			TypeDefinition w = (TypeDefinition) _classes[searchClass];
			if (w == null)
			{
				throw new TypeException("no id for class: " + searchClass.FullName);
			}
			
			if (w.id == - 1)
				throw new TypeException("no id for class");
			
			return w.id;
		}
		
		public virtual System.Type GetTypeClass(int id)
		{
			TypeDefinition def = (TypeDefinition) _types[id];
			if (def == null)
				throw new TypeException("type not found");
			
			return def.clss;
		}
	}
}